package pso;

import java.io.IOException;
import java.util.ArrayList;

import run.FileWrite;

import function.Function;

public class PSO_Focal {

	private Function f;
	private int psoSize;
	private int numberIterations;
	private ArrayList<Particle> population;
	
	private StringBuffer bfInterval = new StringBuffer();
	private StringBuffer bfGlobal   = new StringBuffer();
	private String expName;
	
	
	public PSO_Focal(Function f, int psoSize, int numberIterations, String expName){
		this.f = f;
		this.psoSize = psoSize;
		this.numberIterations = numberIterations;
		this.expName = expName;
	}
	
	
	public void run(){
		
		int iteration = 0;
		this.createPopulation();
		
		
		while(iteration < this.numberIterations){
			
			updatePersonalBestPosition();
			updateFocalPosition();	
			updateVelocity(iteration);
			updatePosition();
			printBestPoint(iteration);
			writeInFile(this.bfInterval , this.expName);
			clearBuffers();
			iteration++;
		}
		
		this.bfGlobal.append(printBestPoint(iteration));
		this.bfGlobal.append(",");
		writeInFile(this.bfGlobal, this.expName + "-fitness");
		
	}
	
	//criando as populacoes
	private void createPopulation() {
		this.population = new ArrayList<Particle>();
		for (int i = 0; i < this.psoSize; i++) {
			population.add(new Particle(this.f,i));
		}
	}
		
		
	private void updateFocalPosition(){
		for (Particle particle : this.population) {
	
			
			if (particle.getIndex() == 0){
				double fitness = f.calculate(particle.getPersonalBestPosition());
				particle.setnBest(particle.getPersonalBestPosition().clone());
				for (Particle p : this.population) {
					if (fitness > this.f.calculate(p.getPersonalBestPosition())){
						particle.setnBest(p.getPersonalBestPosition().clone());
					}
				}
			}
			else{
				double fitnessParticula = f.calculate(particle.getPersonalBestPosition());
				double fitnessParticulaFocal = f.calculate(this.population.get(0).getPersonalBestPosition());
				if (fitnessParticula < fitnessParticulaFocal){
					particle.setnBest(particle.getPersonalBestPosition());
				}
				else {
					particle.setnBest(this.population.get(0).getPersonalBestPosition());
				}
			}
		}	
	}
	
	private void updatePersonalBestPosition(){
		for (Particle particle : population) {
			particle.calculatePersonalBestPosition();
		}
	}
	
	private void updateVelocity(int iteration){
		for (Particle particle : population) {
			particle.calculateVelocity(particle.getnBest(), iteration, this.numberIterations);
		}
	}
	
	private void updatePosition(){
		for (Particle particle : population) {
			particle.calculatePosition();
		}
	}
	
	private double printBestPoint(int iteration) {
		double bestFitness = Double.MAX_VALUE;
		double fitnessParticula;
		for (Particle particle : this.population) {
			fitnessParticula = this.f.calculate(particle.getnBest());
			if (fitnessParticula < bestFitness){
				bestFitness = fitnessParticula;			
			}
		}
		
		if (iteration % ((this.numberIterations-1) / 10  ) == 0){
			bfInterval.append(bestFitness + "\t" + iteration/(double)100 + "\t" + this.expName);
			bfInterval.append("\n");
		}
		return bestFitness;
	}
	
	private void writeInFile(StringBuffer bf , String filename){
		try {
			FileWrite.writeInFile(bf.toString(),"./resultadosPSO//" + filename);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void clearBuffers(){
		this.bfGlobal.delete(0, this.bfGlobal.length());
		this.bfInterval.delete(0, this.bfInterval.length());
	}
	
}
